﻿using CCWin;
using MathNet.Numerics;
using MetroFramework.Forms;
using OutbreakForecast.Model;
using System;
using System.Collections.Generic;
using System.Data;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Windows.Forms;
using System.Windows.Forms.DataVisualization.Charting;

namespace OutbreakForecast
{
    public partial class OutbreakForecastFrm : MetroForm
    {
        /// <summary>
        /// 用于拟合数据的天数
        /// </summary>
        private int useDay = 5;

        private DataHelper dataHelper = new DataHelper();

        public OutbreakForecastFrm()
        {
            InitializeComponent();
            CheckForIllegalCrossThreadCalls = false;

            new Thread(() =>
            {
                while (true)
                {
                    metroLabel1.Text = radioButton1.Checked ? dataHelper.GetChinaNow() : dataHelper.GetGlobalNow();
                    Thread.Sleep(1000 * 60);
                }
            }).Start();

            Run();
        }

        private void Run()
        {
            var dataRow = radioButton1.Checked ? dataHelper.GetChina() : dataHelper.GetGlobal();
            var data = dataRow.OrderBy(i => i.DateTime).ToList();
            if (data.Last().DateTime == DateTime.Today) data.RemoveAt(data.Count - 1);
            toolStripStatusLabel1.Text = data.Last().DateTime.ToString("yyyy-MM-dd") + $" 之后均为预测数据 The data after {data.Last().DateTime.ToString("yyyy-MM-dd")} is predicted";

            var P = Fit(data);
            Predict(P.Pc, P.Ph, P.Pd, data);

            dataGridView1.DataSource = data;

            chart1.Series.Clear();
            new List<string> { "确诊Confirm", "治愈Heal", "死亡Dead" }.ForEach(i => Plot(i, data));
        }

        private void OutbreakForecastFrm_FormClosing(object sender, FormClosingEventArgs e)
        {
            Environment.Exit(0);
        }

        private void radioButton1_CheckedChanged(object sender, EventArgs e)
        {
            metroLabel1.Text = radioButton1.Checked ? dataHelper.GetChinaNow() : dataHelper.GetGlobalNow();
            Run();
        }

        private void numericUpDown1_ValueChanged(object sender, EventArgs e)
        {
            Run();
        }

        private (double[] Pc, double[] Ph, double[] Pd) Fit(List<DataRaw> data)
        {
            var x = new double[useDay];
            var (yc, yh, yd) = (new double[useDay], new double[useDay], new double[useDay]);
            var dataCount = data.Count - useDay;
            for (int i = 0; i < useDay; i++)
            {
                x[i] = i;
                yc[i] = data[dataCount + i].Confirm;
                yh[i] = data[dataCount + i].Heal;
                yd[i] = data[dataCount + i].Dead;
            }
            var Pc = MathNet.Numerics.Fit.Polynomial(x, yc, 1);
            var Ph = MathNet.Numerics.Fit.Polynomial(x, yh, 1);
            var Pd = MathNet.Numerics.Fit.Polynomial(x, yd, 1);
            return (Pc, Ph, Pd);
        }

        private void Predict(double[] Pc, double[] Ph, double[] Pd, List<DataRaw> data)
        {
            for (int i = useDay; i <= useDay + numericUpDown1.Value; i++)
            {
                data.Add(new DataRaw()
                {
                    DateTime = data.Last().DateTime.AddDays(1),
                    confirm = (int)Polynomial.Evaluate(i, Pc),
                    heal = (int)Polynomial.Evaluate(i, Ph),
                    dead = (int)Polynomial.Evaluate(i, Pd)
                });
            }
        }

        private void Plot(string name, List<DataRaw> data)
        {
            var series = new Series() { Name = name, ChartType = SeriesChartType.Point };
            switch (name)
            {
                case "确诊Confirm": data.ForEach(i => series.Points.AddXY(i.DateTime, i.Confirm)); break;
                case "治愈Heal": data.ForEach(i => series.Points.AddXY(i.DateTime, i.Heal)); break;
                case "死亡Dead": data.ForEach(i => series.Points.AddXY(i.DateTime, i.Dead)); break;
            }
            chart1.Series.Add(series);
        }

        private void metroButton1_Click(object sender, EventArgs e)
        {
            using (var yc = new SaveFileDialog())
            {
                yc.Title = "导出预测数据到文件";
                yc.Filter = "CSV文件(*.csv)|*.csv";
                yc.FileName = "预测数据" + DateTime.Now.ToString("yyyy-MM-dd_HH-mm") + ".csv";
                if (yc.ShowDialog() == DialogResult.OK)
                {
                    DoOperation(() =>
                    {
                        StringBuilder sb = new StringBuilder();
                        sb.AppendLine(string.Join(",", dataGridView1.Columns.Cast<DataGridViewColumn>().Select(p => p.HeaderText)));
                        foreach (DataGridViewRow row in dataGridView1.Rows)
                        {
                            sb.AppendLine(string.Join(",", row.Cells.Cast<DataGridViewCell>().Select(p => p.Value)));
                        }
                        File.WriteAllText(yc.FileName, sb.ToString(), Encoding.GetEncoding("GBK"));
                        MessageBoxEx.Show("导出成功", "提示-成功", MessageBoxButtons.OK, MessageBoxIcon.Information);
                    });
                }
            }
        }

        private void DoOperation(Action operation)
        {
            try
            {
                operation();
            }
            catch (Exception ex)
            {
                MessageBoxEx.Show("操作失败！" + ex.ToString(), "提示", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }

        /// <summary>
        /// 鼠标双击导出chart表格为图片
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void chart1_MouseDoubleClick(object sender, MouseEventArgs e)
        {
            SaveFileDialog saveFile = new SaveFileDialog();
            saveFile.Filter = "JPEG文件|*.jpg";
            if (saveFile.ShowDialog() == DialogResult.OK)
            {
                chart1.SaveImage(saveFile.FileName,System.Drawing.Imaging.ImageFormat.Jpeg);
            }
            _ = MessageBoxEx.Show(text: "导出成功", caption: "提示");
        }
    }
}